#include <stdio.h>
#include "core/reader/json_reader.h"

// samsting :
//   porting from aliyun-java-sdk-core/src/main/java/com/aliyuncs/reader/JsonReader.java
//   it's ugliness, but working ok.

namespace aliyun {

JsonReader::JsonReader() {
}

void JsonReader::Parse(const char* buffer, size_t buffer_length, const string& base_key, std::map<string, string>& map) {
  if(NULL == buffer || 0 == buffer_length) {
    return;
  }
  buffer_ = buffer;
  buffer_length_ = buffer_length;
  buffer_index_ = 0;

  c_ = CurrentChar();
  ReadJson(base_key, map);
}

JsonReader::Token JsonReader::ReadJson(const string& base_key, std::map<string, string>& map) {
  SkipWhiteSpace();
  int ch = c_;
  NextChar();
  switch (ch) {
  case '{': ProcessObject(base_key, map); break;
  case '}': token_.type = kTokenTypeObjectEnd; token_.value=""; break;
  case '[': 
    if(c_ == '"') {
      ProcessList(base_key, map);
    } else {
      ProcessArray(base_key, map);
    }
    break;
  case ']': token_.type = kTokenTypeArrayEnd; token_.value=""; break;
  case '"': token_ = ProcessString(); break;
  case ',': token_.type = kTokenTypeComma; token_.value=""; break;
  case ':': token_.type = kTokenTypeColon; token_.value=""; break;
  case 't': // true ?
    NextChar(); NextChar(); NextChar(); 
    token_.type = kTokenTypeBool;
    token_.value = "true";
    break;
  case 'n': // null ?
    NextChar(); NextChar(); NextChar(); 
    token_.type = kTokenTypeNull;
    token_.value = "null";
    break;
  case'f': // false ?
    NextChar(); NextChar(); NextChar(); NextChar(); 
    token_.type = kTokenTypeBool;
    token_.value = "false";
    break;
  default:
    c_ = PrevChar();
    if (('0' <= c_ && c_ <= '9') || c_ == '-') {
      token_ = ProcessNumber();
    }
  }
  return token_;
}

void JsonReader::ProcessObject(const string& base_key, std::map<string, string>& map) {
  string key = base_key + "."+ ReadJson(base_key, map).value;
  while(token_.type != kTokenTypeObjectEnd) {
    ReadJson(key, map);
    if (token_.type != kTokenTypeObjectEnd) {
      Token object = ReadJson(key, map);
      if(object.type == kTokenTypeString || object.type == kTokenTypeNumber || object.type == kTokenTypeBool) {
        map.insert(std::make_pair(key, object.value));
      }
      if (ReadJson(key, map).type == kTokenTypeComma) {
        key = base_key + "." + ReadJson(key, map).value;
      }
    }
  }
}

void JsonReader::ProcessList(const string& base_key, std::map<string, string>& map) {
  Token value = ReadJson(base_key, map);
  int index = 0;
  char n[32];
  while (token_.type != kTokenTypeArrayEnd) {
    sprintf(n, "%d", index);index++;
    string key = TrimFromLast(base_key, ".") + "["+ n +"]"; 
    map.insert(std::make_pair(key, value.value));
    if (ReadJson(base_key, map).type == kTokenTypeComma) {
      value = ReadJson(base_key, map);
    }
  }
  sprintf(n, "%d", index);
  map.insert(std::make_pair(TrimFromLast(base_key, ".") + ".Length", string(n)));
}

void JsonReader::ProcessArray(const string& base_key, std::map<string, string>& map) {
  int index = 0;
  char n[32];
  string pre_key = base_key.substr(0, base_key.find_last_of("."));
  sprintf(n, "%d", index);
  string key = pre_key + "["+ n +"]";
  Token value = ReadJson(key, map);

  while (token_.type != kTokenTypeArrayEnd) {
    sprintf(n, "%d", index+1);
    //map.insert(std::make_pair(pre_key + ".Length" , string(n)));
	map[pre_key + ".Length"] = string(n);
    if(value.type == kTokenTypeString) {
      map.insert(std::make_pair(key, value.value));
    }
    if(ReadJson(base_key, map).type == kTokenTypeComma) {
      index++;
      sprintf(n, "%d", index);
      key = pre_key + "["+ n +"]";
      value = ReadJson(key, map);
    }
  }
}

JsonReader::Token JsonReader::ProcessNumber() {
  string_buffer = "";
  if ('-' == c_) {
    AddChar();
  }
  AddDigits();
  if ('.' == c_) {
    AddChar();
    AddDigits();
  }
  if ('e' == c_ || 'E' == c_) {
    AddChar();
    if ('+' == c_  || '-' == c_) {
      AddChar();
    }
    AddDigits();
  }
  return Token(kTokenTypeString, string_buffer);
}

void JsonReader::AddDigits(void) {
  while (isdigit(c_)) {
    AddChar();
  }
}

void JsonReader::SkipWhiteSpace(void) {
  while(isspace(c_)) {
    NextChar();
  }
}

int JsonReader::CurrentChar(int* length) {
  int len = 0, mask = 0;
  unsigned char ch = buffer_[buffer_index_];
  if(ch < 128) {
    len = 1; mask = 0x7f;
  } else if((ch & 0xe0) == 0xc0) {
    len = 2; mask = 0x1f;
  } else if((ch & 0xf0) == 0xe0) {
    len = 3; mask = 0x0f;
  } else if((ch & 0xf8) == 0xf0) {
    len = 4; mask = 0x07;
  } else if((ch & 0xfc) == 0xf8) {
    len = 5; mask = 0x03;
  } else if((ch & 0xfe) == 0xfc) {
    len = 6; mask = 0x01;
  } else { // error, maybe not utf8
    if(NULL != length) {
      *length = 0;
    }
    return -1;
  }
  if(buffer_index_ + len > buffer_length_) { // error, maybe not utf8
    if(NULL != length) {
      *length = 0;
    }
    return -1;
  }
  unsigned int result = (ch & mask);
  for(int i = 1; i < len; i++) {
    if ((buffer_[buffer_index_ + i] & 0xc0) != 0x80) { // error, maybe not utf8
      if(NULL != length) {
        *length = 0;
      }
      return -1;
    }
    result <<= 6;
    result |= (buffer_[buffer_index_ + i] & 0x3f);
  }
  if(NULL != length) {
    *length = len;
  }
  return (int)result;
}

int JsonReader::NextChar(void) {
  int length = 0;
  if(CurrentChar(&length) < 0) {
    return -1;
  }
  if(buffer_index_ + length < buffer_length_) {
    buffer_index_+= length;
    c_ = CurrentChar();
  } else {
    c_ = -1;
  }
  return c_;
}

int JsonReader::PrevChar(void) {
  int index;
  for(index = buffer_index_ - 1; index >= 0; index--) {
    if ((buffer_[index] & 0xc0) != 0x80) {
      break;
    }
  }
  if(index < 0) { // error, maybe not utf8
    return -1;
  }
  buffer_index_ = index;
  return CurrentChar();
}

static const char s_escapes[][2] = {
  {'\\', '\\'},
  {'/' , '/' },
  {'"' , '"' },
  {'t' , '\t'},
  {'n' , '\n'},
  {'r' , '\r'},
  {'b' , '\b'},
  {'f' , '\f'}
};

JsonReader::Token JsonReader::ProcessString(void) {
  string_buffer = "";
  while (c_ != '"') {
    if (c_ == '\\') {
      NextChar();
      for(size_t i = 0; i < (sizeof(s_escapes)/sizeof(s_escapes[0])); i++) {
        if(s_escapes[i][0] == c_) {
          AddChar(s_escapes[i][1]);
          break;
        }
      }
    } else {
        AddChar();
    }
  }
  NextChar();
  return Token(kTokenTypeString, string_buffer);
}

void JsonReader::AddChar(int ch) {
  int mask, len;
  if (ch < 0x80) {
    mask = 0;
    len = 1;
  } else if (ch < 0x800) {
    mask = 0xc0;
    len = 2;
  } else if (ch < 0x10000) {
    mask = 0xe0;
    len = 3;
  } else if (ch < 0x200000) {
    mask = 0xf0;
    len = 4;
  } else if (ch < 0x4000000) {
    mask = 0xf8;
    len = 5;
  } else {
    mask = 0xfc;
    len = 6;
  }
  char buffer[8];
  for(int i = len - 1; i > 0; i--) {
    buffer[i] = (ch & 0x3f) | 0x80;
    ch = ((unsigned int)ch) >> 6;
  }
  buffer[0] = ch | mask;
  buffer[len] = '\0';
  string_buffer += buffer;
  NextChar();
}

void JsonReader::AddChar(void) {
  AddChar(c_);
}

string JsonReader::TrimFromLast(const string& str, const string& strip_string) {
  int pos = str.find_last_of(strip_string);
  if (pos > -1) {
    return str.substr(0, pos);
  } else {
    return str;
  }
}

} // namespace aliyun {

// end of file
